package co.fitstart.admin.handler.impl;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.demo2do.core.utils.CollectionUtils;
import com.demo2do.core.utils.DateFormatUtils;
import com.demo2do.core.utils.DateUtils;
import com.demo2do.core.utils.StringUtils;

import co.fitstart.admin.entity.report.CoachDaySchedule;
import co.fitstart.admin.entity.report.HourlySchedule;
import co.fitstart.admin.entity.report.WeeklySchedule;
import co.fitstart.admin.handler.ScheduleReportHandler;
import co.fitstart.entity.site.Site;

/**
 * 
 * @author Ysy
 *
 */
@Component("scheduleReportHandler")
public class ScheduleReportHandlerImpl implements ScheduleReportHandler {

    private static final Logger logger = LoggerFactory.getLogger(ScheduleReportHandlerImpl.class);
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.handler.ScheduleReportHandler#generateWeeklySchedules(java.lang.String, java.util.List, java.util.Date, java.util.Date, java.util.List)
     */
    public List<WeeklySchedule> generateWeeklySchedules(String view, List<HourlySchedule> hourlySchedules, Date beginDate, Date endDate, List<String> coaches) {
        
        List<WeeklySchedule> result = new ArrayList<WeeklySchedule>();

        //key:beginDate-endDate  value:list<day>
        Map<String, List<String>> days = splitDatesToWeeks(beginDate, endDate);
        
        for(Map.Entry<String, List<String>> item: days.entrySet()){
            
            Map<String, CoachDaySchedule> dayScheduleMap = new LinkedHashMap<String, CoachDaySchedule>();
            
            String weekDesc = item.getKey();
            
            if(view.equalsIgnoreCase("day")){
                dayScheduleMap = generateDaySchedule(item.getValue(), hourlySchedules, coaches);
            }
            if(view.equalsIgnoreCase("coach")){
                dayScheduleMap = generateCoachSchedule(item.getValue(), hourlySchedules, coaches);
            }
                
            WeeklySchedule weeklySchedule = WeeklySchedule.newInstance(weekDesc, dayScheduleMap);
            result.add(weeklySchedule);
        }
        
        return result;
    }
    
    /**
     * 
     * @param beginDate
     * @param endDate
     * @return
     */
    private Map<String, List<String>> splitDatesToWeeks(Date beginDate, Date endDate){
        
        int weekOffset = DateUtils.weeksBetween(beginDate, endDate) +1;
        
        Map<String, List<String>> dates = new LinkedHashMap<String, List<String>>(weekOffset);
        
        //weekDesc as key:startDate-stopDate
        String weekDesc = null;
        
        //weekDates as value:list of dates in a week
        List<String> weekDates = new ArrayList<String>();
        
        Date mondayOfBeginDate = DateUtils.getFirstWeekDay(beginDate);
        
        Date sundayOfBeginDate = DateUtils.getDayOfWeek(beginDate, Calendar.SUNDAY);
        
        Date startDateOfWeek = beginDate;
        
        Date stopDateOfWeek = endDate.before(sundayOfBeginDate) ? endDate : sundayOfBeginDate;
        
        weekDesc = StringUtils.connect(DateFormatUtils.format(startDateOfWeek, "yyyy-MM-dd") + "-" + DateFormatUtils.format(stopDateOfWeek, "yyyy-MM-dd"));
        
        weekDates = format(DateUtils.getIntervalDays(startDateOfWeek, stopDateOfWeek));
        
        dates.put(weekDesc, weekDates);
        
        for(int i = 1; i< weekOffset; i++) {
            startDateOfWeek = DateUtils.addWeeks(mondayOfBeginDate, i);
            
            if(i == (weekOffset-1)){
                stopDateOfWeek = endDate;
                weekDates = format(DateUtils.getIntervalDays(startDateOfWeek, endDate));
            }
            else{
                stopDateOfWeek = DateUtils.addDays(startDateOfWeek, 6);
                weekDates = format(DateUtils.getIntervalDays(startDateOfWeek, stopDateOfWeek));
            }
            
            weekDesc = StringUtils.connect(DateFormatUtils.format(startDateOfWeek, "yyyy-MM-dd") + "-" + DateFormatUtils.format(stopDateOfWeek, "yyyy-MM-dd"));
            dates.put(weekDesc, weekDates);
        }
        
        return dates;
    }
    
    /**
     * 
     * @param dates
     * @return
     */
    private List<String> format(List<Date> dates){
        List<String> stringDates = new ArrayList<String>();
        
        for(Date date : dates){
            String stringDate = DateFormatUtils.format(date, "yyyy-MM-dd");
            stringDates.add(stringDate);
        }
        
        return stringDates;
    }

    /**
     * 
     * @param days
     * @param hourlySchedules
     * @param coaches
     * @return
     */
    private Map<String, CoachDaySchedule> generateDaySchedule(List<String> days, List<HourlySchedule> hourlySchedules, List<String> coaches) {
        //key:day    value: daySchedule (coach day Map<coach, list<hourlySchedule>>)
        Map<String, CoachDaySchedule> result = new LinkedHashMap<String, CoachDaySchedule>();
        //key: day  value:list
        Map<String, List<HourlySchedule>> hourlySchedulesMap = CollectionUtils.constructMapValuesFromList(hourlySchedules, "day");
        
        for(String day : days){
            
            List<HourlySchedule> hourlySchedulesOneDay = hourlySchedulesMap.get(day);
            Map<String, List<HourlySchedule>> map = CollectionUtils.constructMapValuesFromList(hourlySchedulesOneDay, "coach");
            CoachDaySchedule daySchedule = new CoachDaySchedule(null, day, map);
            result.put(day, daySchedule);
        }
        
        return result;
    }
    
    /**
     * 
     * @param days
     * @param hourlySchedules
     * @param coaches
     * @return
     */
    private Map<String, CoachDaySchedule> generateCoachSchedule(List<String> days, List<HourlySchedule> hourlySchedules, List<String> coaches) {
        //key:coach    value: coachSchedule (coach day Map<day, list<hourlySchedule>>)
        Map<String, CoachDaySchedule> result = new LinkedHashMap<String, CoachDaySchedule>();
        //key: coach  value:list
        Map<String, List<HourlySchedule>> hourlySchedulesMap = CollectionUtils.constructMapValuesFromList(hourlySchedules, "coach");
        
        for(String coach : coaches){
            
            List<HourlySchedule> hourlySchedulesOfCoach = hourlySchedulesMap.get(coach);
            Map<String, List<HourlySchedule>> hourlySchedulesWeekDays = new LinkedHashMap<String, List<HourlySchedule>>();
            Map<String, List<HourlySchedule>> hourlySchedulesAllDays = CollectionUtils.constructMapValuesFromList(hourlySchedulesOfCoach, "day");
            
            for(String day : days){
                for(Map.Entry<String, List<HourlySchedule>> item : hourlySchedulesAllDays.entrySet()){
                    if(item.getKey().equals(day))
                        hourlySchedulesWeekDays.put(item.getKey(), item.getValue());
                }
            }
            
            result.put(coach, new CoachDaySchedule(coach, null, hourlySchedulesWeekDays));
        }
        return result;
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.handler.ScheduleReportHandler#generateHourlySchedules(java.util.List, java.util.List)
     */
    public List<HourlySchedule> generateHourlySchedules(List<HourlySchedule> courseSchedules,
            List<HourlySchedule> restSchedules, Date beginDate, Date endDate, List<String> coaches, Site site) {
        
        //completeSchedule
        List<HourlySchedule> result = completeSchedule(beginDate, endDate, coaches, site);
        
        if(courseSchedules.isEmpty()&&restSchedules.isEmpty()){
            logger.trace("ScheduleReportHandler###generateHourlySchedule - restSchedules && courseSchedules:null");
            return result;
        }
        
        //1.split courseSchedules
        if(!courseSchedules.isEmpty()){
          courseSchedules = splitCourseSchedule(courseSchedules);
          logger.trace("ScheduleReportHandler###generateHourlySchedule - size of courseSchedules:[{}]", courseSchedules.size());
        }
        
        //2.merge restSchedules
        if(!restSchedules.isEmpty()){
          restSchedules = mergeRestSchedule(restSchedules);
          logger.trace("ScheduleReportHandler###generateHourlySchedule - size of restSchedules:[{}]", restSchedules.size());
        }
        
        //3.map
        Map<String, HourlySchedule> resultMap = CollectionUtils.constructMapValueFromList(result, "dhc");
        Map<String, HourlySchedule> courseScheduleMap = CollectionUtils.constructMapValueFromList(courseSchedules, "dhc");
        Map<String, HourlySchedule> restScheduleMap = CollectionUtils.constructMapValueFromList(restSchedules, "dhc");
        result.clear();
        
        //4.combine
        for(String key : resultMap.keySet()){
            if(courseScheduleMap.containsKey(key))
                resultMap.put(key, courseScheduleMap.get(key));
            
            if(restScheduleMap.containsKey(key) && resultMap.get(key).getScheduleType().equals("可上课"))
                resultMap.put(key, restScheduleMap.get(key));
            
            result.add(resultMap.get(key));
        }
        
        return result;
    }
    
    /**
     * 
     * @param beginDate
     * @param endDate
     * @param coaches
     * @param site
     * @return
     */
    private List<HourlySchedule> completeSchedule(Date beginDate, Date endDate, List<String> coaches, Site site){
        List<Date> dates = DateUtils.getIntervalDays(beginDate, endDate);
        List<HourlySchedule> completedSchedule = new ArrayList<HourlySchedule>();
        for(String coach : coaches)
            for(Date day : dates)
                for(String hour : site.getBusinessTimes()){

                    completedSchedule.add(new HourlySchedule(DateFormatUtils.format(day, "yyyy-MM-dd"), coach, hour, "可上课"));
                }
                    
        return completedSchedule;
    }
    
    /**
     * 
     * @param courseSchedules
     * @return
     */
    private List<HourlySchedule> splitCourseSchedule(List<HourlySchedule> courseSchedules){
        List<HourlySchedule> result = new ArrayList<HourlySchedule>();
        
        for(HourlySchedule hourlySchedule : courseSchedules){
            result.add(hourlySchedule);
            result.add(hourlySchedule.addHalfHour());
        }
        
        return result;
    }
    
    /**
     * 
     * @param restSchedules
     * @return
     */
    private List<HourlySchedule> mergeRestSchedule(List<HourlySchedule> restSchedules){
        List<HourlySchedule> result = new ArrayList<HourlySchedule>();
        Map<String, List<HourlySchedule>> restScheduleMap = CollectionUtils.constructMapValuesFromList(restSchedules, "dhc");
        
        for(String key : restScheduleMap.keySet()){
            
            List<HourlySchedule> hourlySchedules = restScheduleMap.get(key);
            if(hourlySchedules.size() > 1) {
                for(HourlySchedule hourlySchedule : hourlySchedules){
                    if(!hourlySchedule.isDuty())
                        result.add(hourlySchedule);
                }
            }else {
                result.add(hourlySchedules.get(0));
            }
        }
        return result;
    }

}
